package com.boat.framework.common.sequence;

import java.util.Date;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.boat.framework.common.util.SpringUtils;

import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;

/**
 * id序列号工厂
 * 
 * @author 周江
 *
 */
public class SequenceFactory {

	private static Logger logger = LoggerFactory.getLogger(SequenceFactory.class);

	/** 存当前不同类型ID值 **/
	private static Map<String, SequenceInfo> currSeqMap = new ConcurrentHashMap<String, SequenceInfo>();

	private volatile static SequenceFactory instance;

	private SequenceFactory() {

	}

	public static SequenceFactory instance() {
		if (instance == null) {
			synchronized (SequenceFactory.class) {
				// 双重检查，避免并发访问创建多个对象问题
				if (instance == null) {
					instance = new SequenceFactory();
				}
			}
		}

		return instance;
	}

	/***
	 * 说明:获取表唯一ID,为19位字符，(表类型3位+日期6位+数据库sequence 6位+内存自增记数4位)
	 */
	public String getId(UniqueIdCode idCode) {
		return getId(idCode.getCode(), idCode.getSequenceName());
	}

	public String getId(String code, String sequenceName) {
		code = StrUtil.nullToEmpty(code).toUpperCase();
		sequenceName = StrUtil.nullToEmpty(sequenceName).toUpperCase();
		checkParam(code, sequenceName);

		StringBuilder strBuff = new StringBuilder(19);
		strBuff.append(code); // +3
		strBuff.append(DateUtil.format(new Date(), DatePattern.PURE_DATE_FORMATTER)); // +6
		long value = getNextValue(sequenceName, 4);// +10
		strBuff.append(StrUtil.fillBefore(String.valueOf(value), '0', 10));
		return strBuff.toString();
	}

	private void checkParam(String code, String sequenceName) {
		if (StrUtil.isBlank(code) || StrUtil.isBlank(sequenceName)) {
			throw new IllegalArgumentException("code或sequenceName参数为空");
		}
		if (3 != code.length()) {
			throw new IllegalArgumentException("code格式不正确");
		}
		if (!sequenceName.startsWith("SEQ_")) {
			throw new IllegalArgumentException("sequenceName格式不正确");
		}
	}

	/***
	 * 根据指定的sequenceCode 获取序列值
	 * 
	 * @param sequenceName
	 * @param size         缓存到内存中的位数
	 * @return long
	 */
	protected long getNextValue(String sequenceName, int size) {
		long memSeqLimit = (long) Math.pow(10, size);

		SequenceInfo sequenceInfo = currSeqMap.get(sequenceName);
		if (sequenceInfo == null) {
			synchronized (currSeqMap) {
				sequenceInfo = currSeqMap.get(sequenceName);
				if (sequenceInfo == null) {
					sequenceInfo = new SequenceInfo();
					currSeqMap.put(sequenceName, sequenceInfo);
				}
			}
		}

		long memSeqValue = sequenceInfo.memSeq.incrementAndGet();
		long dbSeqValue = sequenceInfo.dbSeq;
		// 如果判断当前号段是否用完，用完则从数据库取新号段
		while (memSeqValue >= memSeqLimit || dbSeqValue <= 0) {
			synchronized (sequenceInfo) {
				if (dbSeqValue == sequenceInfo.dbSeq) {
					sequenceInfo.dbSeq = getDbSeqValue(sequenceName);
					memSeqValue = 1;
					sequenceInfo.memSeq.set(memSeqValue);
				} else {
					memSeqValue = sequenceInfo.memSeq.incrementAndGet();
				}

				dbSeqValue = sequenceInfo.dbSeq;
			}
		}

		return (dbSeqValue * memSeqLimit) + memSeqValue;
	}

	/**
	 * 说明: 根据指定的序列值获取值
	 */
	private long getDbSeqValue(String sequenceName) {
		SequenceStore sequenceStore = SpringUtils.getBean("sequenceStore");
		try {
			return sequenceStore.getSequenceValue(sequenceName);
		} catch (Exception e1) {
			for (int i = 0; i < 3; i++) {
				try {
					Random random = new Random();
					long randomMs = random.nextInt(1000) + 1000;
					Thread.sleep(randomMs);
					return sequenceStore.getSequenceValue(sequenceName);
				} catch (Exception e2) {
					logger.error("第" + i + "次获取失败");
				}
			}

			throw e1;
		}
	}

	static class SequenceInfo {
		long dbSeq = -1;
		AtomicLong memSeq = new AtomicLong(-1);
	}

}
